from ...zlgcan.zlgcan import *
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
from ...zuds.zuds import *
from ...utils.types import TestData_Type, Uds_Request_Type, CanFDMsg_std_type, CanMsg_std_type
from ...utils.loggers import dev_logger, record_logger, prod_logger, ic_debugger


from .notebook_tabs.can_unittest_lib import Can_Unittesst_Tab_Frame
from .notebook_tabs.can_manualTab_Frame import Can_ManualTab_Frame
from .notebook_tabs.uds_autoUnitTab_Frame import Uds_autoUnitTab_Frame
from .notebook_tabs.modelTab_Frame import ModelTab_Frame
from .notebook_tabs.uds_unitTab_Frame import Uds_unitTab_Frame
from .notebook_tabs.batc_seat_unittest_lib import Can_Batc_Unittesst_Tab_Frame

from ...utils.msg_queue import global_can_unitTestRecvQueues

from ...globals import GLOBAL_UDS_SERVE_FUNC_LIB


class UdsManualWindow(tk.Tk):

    sendLabelFrame_height = 150
    sendLabelFrame_width = 150
    recvLabelFrame_height = 150
    recvLabelFrame_width = 150

    def __init__(self) -> None:
        super().__init__()
        self.geometry("1000x600+200+200")
        # self.geometry(str(self.sendLabelFrame_width+self.recvLabelFrame_width) + 'x' +
        #   str(self.sendLabelFrame_height + self.recvLabelFrame_height) + "+200+200")
        self.title('uds手动界面')

        # self.TESTER = Uds_UI_Tester(modelWin=self)
        self.TESTER = None

        # DOC 兼容处理因为此处已经是顶级窗口，所以将masterUI指向自己
        self.masterUI = self

        # 设置后台接收can报文线程
        self.readMsg_thread = None
        # 设置event，event控制什么时候开启接收,这样就不用一直在接收报文，浪费资源
        self.readMsg_event = threading.Event()
        self.readMsg_event.clear()
        # 设置接收存放can信息的queue
        self.recv_queues = global_can_unitTestRecvQueues
        # 设置线程共用的开启计数器
        self.readMsg_event_open_counts = 0

        self.tabControlInit()

        # 开启后台接收can报文线程
        self.can_readMsg_thread_init()

    def set_tester(self, tester):
        # 创建窗口时并没有管理各种的tester和Workflow，避免了其他地方全局使用masterUI出现循环引用问题
        self.TESTER = tester

    def set_zcanlib(self, zcanlib, device_handle):
        self.zcanlib = zcanlib
        self.device_handle = device_handle

    def tabControlInit(self):
        self.tabcontrol = ttk.Notebook(self)
        self.tabcontrol.grid(row=1, columnspan=5, sticky="nw")
        self.uds_manualTab = tk.Frame(self.tabcontrol)

        # self.uds_unitTab = tk.Frame(self.tabcontrol)
        self.uds_unitTab = Uds_unitTab_Frame(
            master=self.tabcontrol, masterUI=self.masterUI, parentWin=self)

        # self.modelTab = tk.Frame(self.tabcontrol)
        self.modelTab = ModelTab_Frame(
            master=self.tabcontrol, masterUI=self.masterUI, parentWin=self)

        # self.uds_autoUnitTab = tk.Frame(self.tabcontrol)
        self.uds_autoUnitTab = Uds_autoUnitTab_Frame(
            master=self.tabcontrol, masterUI=self.masterUI, parentWin=self)

        # self.can_manualTab = tk.Frame(self.tabcontrol)
        self.can_manualTab = Can_ManualTab_Frame(
            master=self.tabcontrol, masterUI=self.masterUI, parentWin=self)

        # self.can_unitTab = tk.Frame(self.tabcontrol)
        self.can_unitTab = Can_Unittesst_Tab_Frame(
            master=self.tabcontrol, masterUI=self.masterUI, parentWin=self)

        self.can_batc_unitTab = Can_Batc_Unittesst_Tab_Frame(
            master=self.tabcontrol, masterUI=self.masterUI, parentWin=self
        )

        self.tabcontrol.add(self.uds_manualTab, text="uds手动发送")
        self.tabcontrol.add(self.modelTab, text="模块测试库")
        self.tabcontrol.add(self.uds_unitTab, text="uds单元测试库")
        self.tabcontrol.add(self.uds_autoUnitTab, text="uds自动测试库")
        self.tabcontrol.add(self.can_manualTab, text="can手动操作")
        self.tabcontrol.add(self.can_unitTab, text="can单元测试库")
        self.tabcontrol.add(self.can_batc_unitTab, text="can_Batc单元测试库")

        self.uds_manualTabInit()

    def uds_manualTabInit(self):
        self.manualTabViewInit()


############################################################
# DOC: 手动主界面初始化开始#######################################
############################################################


    def manualTabViewInit(self):
        self.sendLabelFrame = tk.LabelFrame(
            self.uds_manualTab, height=self.sendLabelFrame_height, width=self.sendLabelFrame_width, text="uds发送回显")
        self.sendLabelFrame.grid(row=1, column=1, sticky=tk.NSEW)
        self.sendLabelFrameWidgetInit()
        pass

        self.recvLabelFrame = tk.LabelFrame(
            self.uds_manualTab, height=self.recvLabelFrame_height, width=self.recvLabelFrame_width, text='uds响应接收')
        self.recvLabelFrame.grid(row=1, column=2, sticky=tk.NSEW)
        self.recvLabelFrameWidgetInit()

        self.manualSendFrame = tk.LabelFrame(
            self.uds_manualTab, height=150, width=800, text="uds信息设置(16进制)")
        self.manualSendFrame.grid(
            row=2, column=1, columnspan=2, sticky=tk.NSEW)
        self.manualSendFrameWidgetInit()

        self.sendMsgCycleFrame = tk.LabelFrame(
            self.uds_manualTab, height=50, width=800, text="循环周期发送")
        self.sendMsgCycleFrame.grid(
            row=3, column=1, columnspan=2, sticky=tk.NSEW)
        self.sendMsgCycleFrameWidgetInit()

    def sendLabelFrameWidgetInit(self):
        self.scrollbar_send = tk.Scrollbar(
            self.sendLabelFrame, orient=tk.VERTICAL)
        self.scrollbar_send.grid(row=2, column=2, sticky="ns")
        self.listSendUds = tk.Listbox(
            self.sendLabelFrame,
            font=("Arial", 12),
            height=20,
            width=50,
            yscrollcommand=self.scrollbar_send.set,
        )
        self.listSendUds.grid(row=2, column=1, sticky="nw")
        self.scrollbar_send.config(command=self.listSendUds.yview)

        self.bt_clear_sendUds = tk.Button(
            self.sendLabelFrame,
            text="清空",
            command=self.btn_click_cmd_clearListSendUds,
        )
        self.bt_clear_sendUds.grid(
            row=3, column=1, columnspan=2, padx=2, pady=2)
        pass

    def recvLabelFrameWidgetInit(self):
        self.scrollbar_recv = tk.Scrollbar(
            self.recvLabelFrame, orient=tk.VERTICAL)
        self.scrollbar_recv.grid(row=2, column=2, sticky="ns")
        self.listRecvUds = tk.Listbox(
            self.recvLabelFrame,
            font=("Arial", 12),
            height=20,
            width=50,
            yscrollcommand=self.scrollbar_recv.set,
        )
        self.listRecvUds.grid(row=2, column=1, sticky="nw")
        self.scrollbar_recv.config(command=self.listRecvUds.yview)

        self.bt_clear_recvUds = tk.Button(
            self.recvLabelFrame,
            text="清空",
            command=self.btn_click_cmd_clearListRevUds,
        )
        self.bt_clear_recvUds.grid(
            row=3, column=1, columnspan=2, padx=2, pady=2)
        pass

    def btn_click_cmd_clearListSendUds(self):
        # messagebox.showinfo(message=' btn_click_cmd_clearListSendUds')
        self.listSendUds.delete(0, tk.END)

    def btn_click_cmd_clearListRevUds(self):
        # messagebox.showinfo(message=' btn_click_cmd_clearListRevUds')
        self.listRecvUds.delete(0, tk.END)

    def manualSendFrameWidgetInit(self):
        # req_srcAddr输入
        self.req_srcAddr_label = tk.Label(self.manualSendFrame, text='req源地址')
        self.req_srcAddr_label.grid(row=1, column=1)
        self.req_srcAddr_entry = tk.Entry(self.manualSendFrame, width=10)
        self.req_srcAddr_entry.grid(row=1, column=2)
        self.req_srcAddr_entry.insert(0, '737')
        # req_dstAddr输入
        self.req_dstAddr_label = tk.Label(
            self.manualSendFrame, text='resp返回地址')
        self.req_dstAddr_label.grid(row=1, column=3)
        self.req_dstAddr_entry = tk.Entry(self.manualSendFrame, width=10)
        self.req_dstAddr_entry.grid(row=1, column=4)
        self.req_dstAddr_entry.insert(0, '7b7')
        pass
        # req sid 输入
        self.req_sid_label = tk.Label(
            self.manualSendFrame, text='服务号sid')
        self.req_sid_label.grid(row=1, column=5)
        self.req_sid_entry = tk.Entry(self.manualSendFrame, width=10)
        self.req_sid_entry.grid(row=1, column=6)
        self.req_sid_entry.insert(0, '22')

        # req_param_len设置
        self.req_param_len_label = tk.Label(
            self.manualSendFrame, text='发送参数长度')
        self.req_param_len_label.grid(row=1, column=7)
        self.req_param_len_entry = tk.Entry(self.manualSendFrame, width=10)
        self.req_param_len_entry.grid(row=1, column=8)
        self.req_param_len_entry.insert(0, '02')

        # req_data_1设置
        self.req_data_label = tk.Label(
            self.manualSendFrame, text='请求参数值(16进制,空格分隔)')
        self.req_data_label.grid(row=1, column=9)
        self.req_data_entry = tk.Entry(self.manualSendFrame, width=30)
        self.req_data_entry.grid(row=1, column=10)
        self.req_data_entry.insert(0, 'f1 90')

        # 单次发送按钮
        # command 是通过tester.work_flow来找到操作函数的，操作函数统一定义在WorkFlow中
        self.req_send_btn_once = tk.Button(
            self.manualSendFrame, text='单次发送', command=self.handle_send_once)
        self.req_send_btn_once.grid(row=2, column=1)

    def handle_send_once(self):
        uds_request = Uds_Request_Type(req_src_addr=int(self.req_srcAddr_entry.get(), 16), req_dst_addr=int(self.req_dstAddr_entry.get(), 16),
                                       req_sid=int(self.req_sid_entry.get(), 16), req_param_len=int(self.req_param_len_entry.get(), 16),
                                       req_data_1=[
                                       int(item, 16) for item in self.req_data_entry.get().split()]
                                       )
        # self.TESTER.WORKFLOW.serveFunc.serve_default(uds_request)
        GLOBAL_UDS_SERVE_FUNC_LIB.serve_default(uds_request)

    def sendMsgCycleFrameWidgetInit(self):
        self.send_cycle_time_label = tk.Label(
            self.sendMsgCycleFrame, text='发送周期时间(秒)')
        self.send_cycle_time_label.grid(row=1, column=1)
        self.send_cycle_time_entry = tk.Entry(self.sendMsgCycleFrame, width=10)
        self.send_cycle_time_entry.grid(row=1, column=2)
        self.send_cycle_time_entry.insert(0, '2')

        self.send_cycle_btn_start = tk.Button(
            self.sendMsgCycleFrame, text='开始发送', command=self.send_cycle_start)
        self.send_cycle_btn_start.grid(row=1, column=3)

        self.func_frame_radioButton_variable = tk.BooleanVar()
        self.func_frame_radioButton1 = tk.Radiobutton(
            self.sendMsgCycleFrame, text='循环关闭', variable=self.func_frame_radioButton_variable, value=False)
        self.func_frame_radioButton1.grid(
            row=1, column=4, padx=10, pady=2, sticky=tk.NSEW)

        self.func_frame_radioButton2 = tk.Radiobutton(
            self.sendMsgCycleFrame, text='循环启动', variable=self.func_frame_radioButton_variable, value=True)
        self.func_frame_radioButton2.grid(
            row=1, column=5, padx=10, pady=2, sticky=tk.NSEW)

    def send_cycle_start(self):
        cycle = float(self.send_cycle_time_entry.get())

        def wrapper():
            self.handle_send_once()
            while self.func_frame_radioButton_variable.get():
                time.sleep(cycle)
                self.handle_send_once()

            messagebox.showinfo('info', '退出循环...')

        th = threading.Thread(target=wrapper)
        th.start()


############################################################
# DOC: 手动主界面初始化完成#######################################
############################################################


############################################################
# DOC: 后台接收can原始报文线程#######################################
############################################################

    def can_readMsg_thread_init(self):
        self.readMsg_thread = threading.Thread(
            None, target=self.backend_MsgReadThreadFunc)
        self.readMsg_thread.start()
        pass

    def can_readMsg_thread_open(self):
        self.readMsg_event_open_counts += 1
        if self.readMsg_event_open_counts > 0:
            self.readMsg_event.set()

    def can_readMsg_thread_close(self):
        self.readMsg_event_open_counts -= 1
        if self.readMsg_event_open_counts <= 0:
            self.readMsg_event_open_counts = 0
            self.readMsg_event.clear()

    def backend_MsgReadThreadFunc(self):
        MAX_RCV_NUM = 10
        try:
            # is_set不阻塞线程，适用于非阻塞场景
            # wait() 阻塞现场，可以重启
            while self.readMsg_event.wait():
                can_num = self.zcanlib.GetReceiveNum(
                    self.device_handle, ZLG_CONSTANT.ZCAN_TYPE_CAN)
                canfd_num = self.zcanlib.GetReceiveNum(
                    self.device_handle, ZLG_CONSTANT.ZCAN_TYPE_CANFD)
                if not can_num and not canfd_num:
                    time.sleep(0.005)  # wait 5ms
                    # time.sleep(0.5)
                    continue

                if can_num:
                    while can_num and self.readMsg_event.wait():
                        # ic_debugger(can_num)
                        read_cnt = MAX_RCV_NUM if can_num >= MAX_RCV_NUM else can_num
                        can_msgs, act_num = self.zcanlib.Receive(
                            self.device_handle, read_cnt, MAX_RCV_NUM)
                        if act_num:
                            # [ ]处理msg
                            for i in range(act_num):
                                cur_msg = can_msgs[i].frame
                                # [ ] 子线程中用ic_debugger有bug，待处理
                                # ic_debugger(cur_msg)
                                if 0:
                                    msg_type = (self.CANMsg2View_std(cur_msg))
                                    print(f'[file:{__file__}]=>[line:{(sys._getframe().f_lineno)}]==>{
                                        msg_type.id_str}::{msg_type.data_str}')
                                self.recv_queues.append_msg_to_queues(cur_msg)
                                pass
                        else:
                            break
                        can_num -= act_num
                if canfd_num:
                    while canfd_num and self.readMsg_event.wait():
                        read_cnt = MAX_RCV_NUM if canfd_num >= MAX_RCV_NUM else canfd_num
                        canfd_msgs, act_num = self.zcanlib.ReceiveFD(
                            self.device_handle, read_cnt, MAX_RCV_NUM)
                        if act_num:
                            # [ ] 处理 canfd msg
                            # ic_debugger(canfd_msgs[i].frame)
                            for i in range(act_num):
                                cur_msg_fd = canfd_msgs[i].frame
                                if 1:
                                    msg_type = (
                                        self.CANFDMsg2View_std(cur_msg_fd))
                                    print(f'[file:{__file__}]=>[line:{(sys._getframe().f_lineno)}]==>{
                                        msg_type.id_str}::{msg_type.data_str}')
                                self.recv_queues.append_msg_to_queues(
                                    cur_msg_fd)
                        else:
                            break
                        canfd_num -= act_num
        except:
            print(f'[file:{__file__}]=>[line:{(sys._getframe().f_lineno)
                                              }]==> backend:Error occurred while read CAN(FD) data!')

    def CANMsg2View_base(self, msg):
        '''
        全面解析can msg
        '''
        view = []

        # 提取id号
        view.append(hex(msg.can_id)[2:])

        # 确认标准帧还是扩展帧
        str_info = ''
        str_info += 'EXT' if msg.eff else 'STD'

        # 确认是否远程帧
        if msg.rtr:
            str_info += ' RTR'
        view.append(str_info)

        # data长度
        view.append(str(msg.can_dlc))
        if msg.rtr:
            view.append('')

        else:
            view.append(''.join(hex(msg.data[i])[
                        2:] + ' ' for i in range(msg.can_dlc)))
        return view

    def CANMsg2View_std(self, msg):
        '''
            转换can报文为id 和 data_list
        '''
        id = msg.can_id
        id_str = hex(id)

        data_list = [msg.data[i] for i in range(msg.can_dlc)]
        data_list_str = [f"{item:02x}" for item in data_list]
        data_str = ''.join(data_list_str)

        return CanMsg_std_type(id=id, id_str=id_str, data_list=data_list, data_list_str=data_list_str, data_str=data_str)

    def CANFDMsg2View_base(self, msg):
        view = []

        view.append(hex(msg.can_id)[2:])

        str_info = ''
        str_info += 'EXT' if msg.eff else 'STD'
        if msg.rtr:
            str_info += ' RTR'
        else:
            str_info += ' FD'
            if msg.brs:
                str_info += ' BRS'
            if msg.esi:
                str_info += ' ESI'
        view.append(str_info)
        view.append(str(msg.len))
        if msg.rtr:
            view.append('')
        else:
            view.append(''.join(hex(msg.data[i])[
                        2:] + ' ' for i in range(msg.len)))
        return view

    def CANFDMsg2View_std(self, msg):
        '''
            [ ]该函数未测试，使用前要经过测试
        '''

        id = msg.can_id
        id_str = hex(id)

        data_length = msg.len
        data_list = [msg.data[i] for i in range(data_length)]
        data_list_str = [f"{item:02x}" for item in data_list]
        data_str = ''.join(data_list_str)

        return CanFDMsg_std_type(id=id, id_str=id_str, data_list=data_list, data_list_str=data_list_str, data_str=data_str)


GLOBAL_MASTER_UI = UdsManualWindow()


if __name__ == "__main__":
    demo = UdsManualWindow()
    demo.mainloop()
